package com.yang.strategy.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import static com.yang.constants.RedisConstants.CACHE_NULL_TTL;
import static com.yang.constants.RedisConstants.LOCK_CACHE_KEY;

@Service("queryWithMutex")
public class QueryWithMutex extends AbstractCacheStrategyImpl{

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private RedissonClient redissonClient;

    @Override
    public <R, ID> R getCache(String keyPrefix, ID id, Class<R> type, Function<ID, R> dbFallback, Long time, TimeUnit unit) {
        // 拼接出key
        String key = keyPrefix + id;

        // 判断数据是否存在
        String valueJson = stringRedisTemplate.opsForValue().get(key);

        // 数据存在直接返回
        if (StrUtil.isNotBlank(valueJson)){

            super.setCache(key,valueJson,time,unit,false);

            return JSONUtil.toBean(valueJson,type);
        }

        R data;

        // 数据不存在，获取互斥锁，进行缓存重建
        RLock lock = redissonClient.getLock(LOCK_CACHE_KEY + id);

        lock.lock(5,TimeUnit.SECONDS);

        try {
            data = dbFallback.apply(id);

            if (data == null){
                stringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL,TimeUnit.MINUTES);
                return null;
            }

            super.setCache(key,data,time,unit,false);
        }finally {
            lock.unlock();
        }

        return data;
    }
}
